#pragma once
#include "cache.h"
#include "dram.h"

// #define COUNT_MEMORY_ACCESS_SPACE 1

class L1Cache : public NaiveCache
{
protected:
    enum class CacheLineState
    {
        None,
        Valid,
        Dirty
    };
    typedef struct
    {
        word_t tag;
        CacheLineState state;
    } Meta;

    struct
    {
        bool meta_port_busy;
        bool mem_port_busy;

        bool in_release;
        bool release_wait_ack;
        int victim_way;
        word_t release_index;
        word_t release_tag;
        OP *release_op;
        DramOP *release_dram_op;
        Meta release_meta;
    } state = {0};

    int m_data_size;      /**< cache data size */
    int m_line_size;      /**< cache line size */
    int m_index_size;     /**< cache index size*/
    int m_assoc_way;      /**< associativity */
    int m_offset_bits;    /**< cache offset mask */
    int m_index_bits;     /**< cache set bits */
    int m_tag_bits;       /**< cache tag bits */
    int m_offset_padding; /**< cache offset mask */
    int m_index_padding;  /**< cache set bits */
    int m_tag_padding;    /**< cache tag bits */
    word_t m_offset_mask; /**< cache offset mask */
    word_t m_index_mask;  /**< cache set mask */
    word_t m_tag_mask;    /**< cache tag mask */

    Meta **metas;

    typedef struct
    {
        OP *op;
        DramOP *dram_op;
        bool hit;
        bool miss;
        int hit_way;
        Meta hit_meta;
    } pipeline_latch;

    const pipeline_latch none_latch = {0};

    pipeline_latch s1, s2;

private:
    std::tuple<bool, int, Meta> access_meta(word_t addr);
    bool hazard_check(OP *op);
    bool bypass_check(OP *op);
    inline word_t prase_index(word_t addr)
    {
        return (addr & m_index_mask) >> m_index_padding;
    }
    Counter load_hit_cnt;
    Counter load_miss_cnt;
    Counter store_hit_cnt;
    Counter store_miss_cnt;
    Counter gated_cycle;
public:
    enum class MSHR_State
    {
        RecvLD,
    };
    typedef std::tuple<OP *, MSHR_State, DramOP *> MSHR;

    std::vector<MSHR> mshrs;
    std::list<OP *> store_buffer;
    std::queue<OP *> req;
    std::vector<OP *> resp;

    CacheLineReplacer *replacer;
    Dram *dram;

    L1Cache();
    ~L1Cache();
    void reset() override;
    bool send(OP *op) override;
    bool recv(OP *op) override;
    void run() override;
#if COUNT_MEMORY_ACCESS_SPACE
    uint32_t m_space_ld[8192] = {0};
    uint32_t m_space_st[8192] = {0};
    uint32_t m_space_ld_max = 0;
    uint32_t m_space_st_max = 0;
    uint32_t m_space_ld_cnt = 0;
    uint32_t m_space_st_cnt = 0;
#endif
    void print_counter();
    void print_status();
private:
    void Stage1();
    void Stage2();
    void Stage3();
};